﻿using System;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Xml;
using System.Net.Cache;
using System.Threading;
using System.IO;

namespace QDT.Payment.AllInPay.Api
{
    /// <summary>
    /// 异步请求响应回调委托类型
    /// </summary>
    /// <param name="resp">响应报文对象</param>
    /// 
    public delegate void SendCallback(Response resp);

    /// <summary>
    /// 通联通平台通讯类
    /// </summary>
    public class AIPG
    {

        private string m_url = "";
        private string m_strLastErrMsg;
        private AipgError m_enumLastError = AipgError.NoError;
        private X509Certificate2 m_cerSigneCert = null;  // 签名证书
        private X509Certificate2 m_cerVerifyCert = null;  //验签证书


        /// <summary>
        /// 设置url
        /// </summary>
        public string url
        {
            get { return url; }
            set
            {
                if (value == null)
                    throw new Exception("url不能为空");
                m_url = value;
            }
        }

        /// <summary>
        /// 获取上一次操作错误信息
        /// </summary>
        public string LastErrMsg
        {
            get { return m_strLastErrMsg; }
        }

        /// <summary>
        /// 获取上一次操作的错误信息
        /// </summary>
        public AipgError LastErr
        {
            get { return m_enumLastError; }
        }

        /// <summary>
        /// 获取或设置用于签名的X509证书对象
        /// </summary>
        public X509Certificate2 SigneCert
        {
            get { return m_cerSigneCert; }
            set
            {
                if (value == null || !value.HasPrivateKey)
                    throw new Exception("签名证书必须包含私钥信息");
                m_cerSigneCert = value;
            }
        }
        /// <summary>
        /// 获取或设置用于验签的X509证书对象
        /// </summary>
        public X509Certificate2 VerifyCert
        {
            get { return m_cerVerifyCert; }
            set
            {
                if (value == null || value.PublicKey == null || value.PublicKey.Key == null)
                    throw new Exception("验签证书必须包含公钥信息");
                m_cerVerifyCert = value;
            }
        }

        /// <summary>
        /// 同步发送请求报文至服务器
        /// </summary>
        /// <param name="reqPackage">请求报文对象</param>
        /// <returns>响应报文对象，获取响应失败时返回null</returns>
        public Response Send(Request reqPackage)
        {
            if (m_cerSigneCert == null)
            {
                m_strLastErrMsg = "未设置签名所用证书";
                m_enumLastError = AipgError.SignError;
                return null;
            }
            if (m_cerVerifyCert == null)
            {
                m_strLastErrMsg = "未设置验签所用证书";
                m_enumLastError = AipgError.VerifyError;
                return null;
            }
            reqPackage.Certificate = SigneCert;

            byte[] postData;
            try
            {
                postData = reqPackage.ToGBKBytes();
            }
            catch (Exception e)
            {
                m_enumLastError = AipgError.SignError;
                m_strLastErrMsg = "签名错误:" + e.Message;
                return null;
            }
            HttpWebRequest request = null;
            try
            {
                request = CreateRequest(m_url, "POST");
                reqPackage.BeforeSend();
                postData = reqPackage.ToGBKBytes();

                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postData.Length;
                request.KeepAlive = false;

                Stream reqStream = request.GetRequestStream();
                reqStream.Write(postData, 0, postData.Length);
                reqStream.Close();
            }
            catch
            {
                m_strLastErrMsg = "连接远程服务器失败";
                m_enumLastError = AipgError.ConnectError;
            }

            MemoryStream ms = null;
            string respText;
            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                //request.ClientCertificates.Add(m_cerSigneCert);
                Stream resStream = response.GetResponseStream();
                ms = new MemoryStream();
                byte[] buf = new byte[4096];
                int count;
                while ((count = resStream.Read(buf, 0, buf.Length)) > 0)
                {
                    ms.Write(buf, 0, count);
                }
                resStream.Close();
                respText = Encoding.GetEncoding(InternalConstants.Encode).GetString(ms.ToArray());

            }
            catch (Exception ex)
            {
                m_strLastErrMsg = "获取服务器响应错误:" + ex.Message;
                m_enumLastError = AipgError.ResponseError;
                return null;
            }

            try
            {
                m_strLastErrMsg = "";
                m_enumLastError = AipgError.NoError;
                return ParseResponse(respText);
            }
            catch (Exception ex)
            {
                if (m_enumLastError == AipgError.NoError)
                {
                    m_strLastErrMsg = "解释响应报文错误:" + ex.Message;
                    m_enumLastError = AipgError.ParseError;
                }
                return null;
            }
        }

        /// <summary>
        /// 异步发送请求报文至服务器
        /// </summary>
        /// <param name="request">请求报文对象</param>
        /// <param name="callback">响应回调方法委托</param>
        public void AsynSend(Request request, SendCallback callback)
        {
            Thread thread = new Thread(SendThread);
            thread.Start(new object[] { request, callback });
        }

        private void SendThread(object ps)
        {
            object[] pa = (object[])ps;
            Response resp = Send((Request)pa[0]);
            ((SendCallback)pa[1])(resp);
        }

        public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        { // 总是接受 
            return true;
        }

        private HttpWebRequest CreateRequest(string url, string method)
        {
            Uri uri = new Uri(url);
            //if (uri.Scheme == "https")
            //{
            //   ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
            //}

            //Set a default policy level for the "http:" and "https" schemes.            
            HttpRequestCachePolicy policy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Revalidate);
            HttpWebRequest.DefaultCachePolicy = policy;
            ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);//验证服务器证书回调自动验证  
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

            request.ClientCertificates.Add(m_cerSigneCert);

            request.AllowAutoRedirect = false;
            request.AllowWriteStreamBuffering = false;
            request.Method = method;
            return request;
        }

        private Response ParseResponse(string xmlText)
        {
            XmlDocument xmlDoc = new XmlDocument();
            try
            {
                xmlDoc.LoadXml(xmlText);
            }
            catch
            {
                return null;
            }
            XmlNode typeNode = xmlDoc.SelectSingleNode("/" + InternalConstants.RootNode + "/INFO/TRX_CODE/node()");

            Response resp = null;
            if (typeNode != null)
            {
                string typeStr = typeNode.Value;

                if (InternalConstants.BatchPayTrxCode.Equals(typeStr)) //批量代付交易报文
                {
                    resp = new BatchPayResponse();
                }
                else if (InternalConstants.BatchCollectTrxCode.Equals(typeStr)) //批量代收交易报文
                {
                    resp = new BatchCollectionResponse();
                }
                else if ("100011".Equals(typeStr) || "100014".Equals(typeStr))
                {
                    Response tempresp = new Response();
                    typeNode = xmlDoc.SelectSingleNode("/" + InternalConstants.RootNode + "/INFO/RET_CODE/node()");
                    tempresp.RetCode = typeNode.Value;
                    XmlNode msgNode = xmlDoc.SelectSingleNode("/" + InternalConstants.RootNode + "/INFO/ERR_MSG/node()");
                    XmlNode ret_code = xmlDoc.SelectSingleNode("/" + InternalConstants.RootNode + "/TRANSRET/RET_CODE/node()");
                    XmlNode ret_msg = xmlDoc.SelectSingleNode("/" + InternalConstants.RootNode + "/TRANSRET/ERR_MSG/node()");
                    if (typeNode != null && !"".Equals(typeNode.Value) && "0000".Equals(typeNode.Value))
                    {
                        if (msgNode != null && !"".Equals(msgNode.Value))
                            tempresp.ErrMsg = msgNode.Value;
                        else
                            tempresp.ErrMsg = "交易成功";
                        tempresp.Trans_ret_code = ret_code.Value;
                        if (ret_code != null && "0000".Equals(ret_code.Value))
                        {
                            tempresp.Trans_ret_msg = "交易成功";
                        }
                        else if (ret_msg != null)
                        {
                            tempresp.Trans_ret_msg = ret_msg.Value;
                        }
                        else
                        {
                            tempresp.Trans_ret_msg = "交易失败";
                        }
                    }
                    else
                    {
                        if (ret_code != null)
                            tempresp.Trans_ret_code = ret_code.Value;
                        else
                            tempresp.Trans_ret_code = "3999";
                        if (ret_msg != null)
                            tempresp.Trans_ret_msg = ret_msg.Value;
                        else
                            tempresp.Trans_ret_msg = "交易失败";
                        if (msgNode != null)
                        {
                            tempresp.ErrMsg = msgNode.Value;
                        }
                        else
                            tempresp.ErrMsg = "交易失败";
                    }
                    // 20060400000044502

                    return tempresp;
                }
                else if ("200004".Equals(typeStr))//交易结果查询
                {

                    Response tempresp = new Response();
                    tempresp.Trans_ret_msg = xmlText;
                    return tempresp;
                }

                if (resp != null)
                    resp.ParseXML(xmlDoc);
            }
            if (resp != null)
            {
                // resp.Certificate = null;
                resp.Certificate = m_cerVerifyCert;
                XmlNode signeValue = xmlDoc.SelectSingleNode("/" + InternalConstants.RootNode + "/INFO/SIGNED_MSG/node()");
                string signeText = signeValue == null ? "" : signeValue.Value;
                string unsignedText = Regex.Replace(xmlText, "<SIGNED_MSG>[0-9a-z]*</SIGNED_MSG>", "");

                // 验证签名
                byte[] signe = ToBytes(signeText);
                try
                {
                    if (signe == null || !((RSACryptoServiceProvider)m_cerVerifyCert.PublicKey.Key).VerifyData(
                                                Encoding.GetEncoding(InternalConstants.Encode).GetBytes(unsignedText),
                                                "SHA1",
                                                signe))
                    {
                        m_enumLastError = AipgError.VerifyError;
                        m_strLastErrMsg = "验证签名失败";
                        return null;
                    }
                }
                catch
                {
                    m_enumLastError = AipgError.VerifyError;
                    m_strLastErrMsg = "验证签名失败";
                    return null;
                }
            }
            return resp;
        }

        private byte GetHexValue(char c)
        {
            if (c >= '0' && c <= '9')
                return (byte)(c - '0');
            else if (c >= 'a' && c <= 'f')
                return (byte)(c - 'a' + 10);
            else
                return 255;
        }

        private byte[] ToBytes(string hexStr)
        {
            if ((hexStr.Length & 1) != 0)
                return null;
            byte[] buf = new byte[hexStr.Length / 2];
            for (int i = 0; i < hexStr.Length; i += 2)
            {
                byte h = GetHexValue(hexStr[i]);
                byte l = GetHexValue(hexStr[i + 1]);
                if (h == 255 || l == 255)
                    return null;
                buf[i >> 1] = (byte)((h << 4) | l);
            }
            return buf;
        }

    }
}
